/*
Copyright 2008, 2009 Joonas Lehtolahti

This file is part of Openfire-C.

Openfire-C is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Openfire-C is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Openfire-C.  If not, see <http://www.gnu.org/licenses/>.
*/

int XfireAnalyze_hGC_List(XFINST inst, struct XfirePacket *packet) {
	/* List of saved chat rooms
	 * http://xfireplus.com/xfwiki/index.php/Packet_9b_00
	 *
	 * USES SINGLE-LIST CALLBACK WAY! (sort of...)
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP room_list
	
	LinkedList *room_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x04));
	
	if (room_list) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			ListNode *room_node = room_list->first;
			struct XfChatRoom *xfcr;
			
			while (room_node) {
				xfcr = XfireChatRoom();
				xfcr->gcid = n0scopy((non0string*)room_node->x);
				xfcr->set_saved = 1;
				
				BTreeAdd(inst->chatrooms, xfcr);
				
				NEXT_NODE(room_node);
			}
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(room_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGC_ChangeName(XFINST inst, struct XfirePacket *packet) {
	/* Group chat room name changed
	 * http://xfireplus.com/xfwiki/index.php/Packet_5e_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT non0string*, non0string*
	#define _XFABP gcid_value, name_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	non0string *name_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x05));
	
	if (gcid_value && name_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfChatRoom *xfgc = XfireInst_FindCreateChatRoom(inst, gcid_value);
			if (xfgc) {
				if (xfgc->name) n0sfree(xfgc->name);
				xfgc->name = n0scopy(name_value);
			}
			else {
				/* Failed even creating the chat room? */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_JoinInfo(XFINST inst, struct XfirePacket *packet) {
	/* Group chat room information on join
	 * http://xfireplus.com/xfwiki/index.php/Packet_5f_01
	 * 
	 * POST handler
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int, unsigned int
	#define _XFABP xfgc, *response_value, *reqindex_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *reqindex_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x0b));
	unsigned int *response_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x0c));
	unsigned int *defperm_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x12));
	unsigned int *access_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x17));
	unsigned int *type_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\xaa));
	non0string *name_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x05));
	non0string *motd_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x4d));
	unsigned int *newroom_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\xa5));
	unsigned int *protected_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\xa6));
	unsigned int *silenced_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x16));
	unsigned int *joinleave_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x1b));
	
	if (gcid_value && reqindex_value && response_value && defperm_value &&
		access_value && type_value && name_value && motd_value && newroom_value &&
		protected_value && silenced_value && joinleave_value) {
		
		/* internal handling */
		struct XfChatRoom *xfgc = NULL;
		if (*response_value == XFVALUE_GC_JOIN_SUCCESS) {
			xfgc = XfireInst_FindCreateChatRoom(inst, gcid_value);
			if (xfgc) {
				xfgc->set_defaultperm = *defperm_value;
				xfgc->set_protected = *protected_value;
				xfgc->set_access = *access_value;
				xfgc->set_type = *type_value;
				xfgc->set_silenced = *silenced_value;
				xfgc->set_showjoinpart = *joinleave_value;
				n0sfree(xfgc->name);
				xfgc->name = n0scopy(name_value);
				n0sfree(xfgc->motd);
				xfgc->motd = n0scopy(motd_value);
			}
			else {
				/* Failed even creating the chat room? */
				retval |= XFIH_NONFATAL;
			}
		}
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_Join(XFINST inst, struct XfirePacket *packet) {
	/* Someone joined a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_61_01
	 * 
	 * POST handler
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, struct XfChatRoomUser*
	#define _XFABP xfgc, xfcru
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	non0string *name_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x02));
	non0string *nick_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x0d));
	unsigned int *permlvl_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x12));
	
	if (gcid_value && userid_value && name_value && nick_value && permlvl_value) {
		/* internal handling */
		struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
		if (xfgc) {
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			struct XfChatRoomUser *xfcru;
			if (!xff) {
				/* Unknown person, add to other_people and online_people */
				xff = XfireFriend();
				xff->userid = *userid_value;
				xff->username = n0scopy(name_value);
				BTreeAdd(inst->other_people, xff);
				BTreeAdd(inst->online_people, xff);
			}
			if (inst->self != xff && inst->self->userid == xff->userid) {
				/* Me and myself */
				FreeXfireFriend(inst->self);
				inst->self = xff;
			}
			/* Update nickname, just in case */
			n0sfree(xff->nickname);
			xff->nickname = n0scopy(nick_value);
			/* Add to group chat members list */
			xfcru = XfireChatRoomUser(xff, *permlvl_value);
			BTreeAdd(xfgc->users, xfcru);
			/* Add one to user count */
			xfgc->info_usercount++;
			if (inst->self == xff) {
				xfgc->self = xfcru;
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
		}
		else {
			/* So someone is joining a room we don't know about,
			 * there must be something wrong, mark possible error  */
			retval |= XFIH_NONFATAL;
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_Leave(XFINST inst, struct XfirePacket *packet) {
	/* Someone left a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_62_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, struct XfChatRoomUser*
	#define _XFABP xfgc, xfcru
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	
	if (gcid_value && userid_value) {
		struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
		if (xfgc) {
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			if (xff) {
				struct XfChatRoomUser *xfcru = XfireChatRoomUser(xff, 0);
				TreeNode *gcuser = BTreeFind(xfgc->users, xfcru);
				FreeXfireChatRoomUser(xfcru);
				if (gcuser) {
					xfcru = (struct XfChatRoomUser*)gcuser->x;
				}
				else {
					xfcru = NULL;
				}
				/* include external handlers calling */
				#include "XfirePacketAnalyzeExt.inc"
				
				/* internal handling */
				if (retval & XFIH_HANDLED) {
					if (xfcru) {
						BTreeDel(xfgc->users, gcuser);
						xfgc->info_usercount--;
						FreeXfireChatRoomUser(xfcru);
					}
					else {
						/* The user was not in the chat room? */
						fprintf(stderr, "User not in chat room");
						retval |= XFIH_NONFATAL;
					}
					if (xff == inst->self) {
						xfgc->self = NULL;
					}
				}
			}
			else {
				/* We didn't know this user, something's wrong */
				fprintf(stderr, "User not anywhere");
				retval |= XFIH_NONFATAL;
			}
		}
		else {
			/* So someone is leaving a room we don't know about,
			 * there must be something wrong, mark possible error  */
			fprintf(stderr, "Chat room not anywhere");
			retval |= XFIH_NONFATAL;
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_Message(XFINST inst, struct XfirePacket *packet) {
	/* Someone sent a message to a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_63_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, struct XfChatRoomUser*, non0string*
	#define _XFABP xfgc, xfcru, message_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	non0string *message_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x2e));
	
	if (gcid_value && userid_value && message_value) {
		struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
		if (xfgc) {
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			struct XfChatRoomUser *xfcru = XfireInst_FindChatRoomUserByXfFriend(inst, xfgc, xff);
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
		}
		else {
			/* Chat room not found? */
			retval |= XFIH_NONFATAL;
		}
		
		/* no internal handling */
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_Invite(XFINST inst, struct XfirePacket *packet) {
	/* Someone sent an invite to join a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_64_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT non0string*, unsigned int, unsigned int, non0string*, non0string*, non0string*, unsigned int, unsigned int
	#define _XFABP gcid_value, *type_value, *userid_value, username_value, nickname_value, roomname_value, *access_value, *voice_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *type_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\xaa));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	non0string *username_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x02));
	non0string *nickname_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x0d));
	non0string *roomname_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x05));
	/* attribute 47 */
	unsigned int *access_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x17));
	unsigned int *voice_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x25));
	
	if (gcid_value && type_value && userid_value && username_value &&
		nickname_value && roomname_value && access_value && voice_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* no internal handling */
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ChangeLvl(XFINST inst, struct XfirePacket *packet) {
	/* User's permission level was changed in a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_65_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT non0string*, unsigned int, unsigned int
	#define _XFABP gcid_value, *userid_value, *permission_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x18));
	unsigned int *permission_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x13));
	
	if (gcid_value && userid_value && permission_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
			if (xff && xfgc) {
				struct XfChatRoomUser *xfgcu = XfireInst_FindChatRoomUserByXfFriend(inst, xfgc, xff);
				if (xfgcu) {
					xfgcu->permission = *permission_value;
				}
				else {
					/* User not found in the chat room */
					retval |= XFIH_NONFATAL;
				}
			}
			else {
				/* Unknown user or room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ReqInfo(XFINST inst, struct XfirePacket *packet) {
	/* Info of saved chat rooms
	 * http://xfireplus.com/xfwiki/index.php/Packet_66_01
	 *
	 * USES SINGLE-LIST CALLBACK WAY!
	 * Unblockable internal handling!
	 * 
	 * TODO: NEED POST HANDLER
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP room_list
	
	LinkedList *gcid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x04));
	LinkedList *type_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\xaa));
	LinkedList *name_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x05));
	LinkedList *time_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x14));
	LinkedList *count_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x15));
	LinkedList *access_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x17));
	LinkedList *zero_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x5d));
	
	LinkedList *room_list = LLcreate();
	
	if (gcid_list && type_list && name_list && time_list && count_list &&
		access_list && zero_list) {
		if (gcid_list->nodes == type_list->nodes && type_list->nodes == name_list->nodes &&
			name_list->nodes == time_list->nodes && time_list->nodes == count_list->nodes &&
			count_list->nodes == access_list->nodes && access_list->nodes == zero_list->nodes) {
			
			ListNode *gcid_node = gcid_list->first;
			ListNode *type_node = type_list->first;
			ListNode *name_node = name_list->first;
			ListNode *time_node = time_list->first;
			ListNode *count_node = count_list->first;
			ListNode *access_node = access_list->first;
			/* ListNode *zero_node = zero_list->first; */
			
			while (gcid_node) {
				non0string *gcid = (non0string*)gcid_node->x;
				struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid);
				if (xfcr) {
					xfcr->set_type = *(unsigned int*)type_node->x;
					n0sfree(xfcr->name);
					xfcr->name = n0scopy((non0string*)name_node->x);
					xfcr->info_timestamp = *(unsigned int*)time_node->x;
					xfcr->info_usercount = *(unsigned int*)count_node->x;
					xfcr->set_access = *(unsigned int*)access_node->x;
						
					LLaddLast(room_list, xfcr);
				}
				else {
					/* Chat room not found? Mark possible error but continue. */
					retval |= XFIH_NONFATAL;
				}
				
				NEXT_NODE(gcid_node);
				NEXT_NODE(type_node);
				NEXT_NODE(name_node);
				NEXT_NODE(time_node);
				NEXT_NODE(count_node);
				NEXT_NODE(access_node);
				/* NEXT_NODE(zero_node); */
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				/* Nothing to internal handle anymore... */
			}
			else {
				/* Whoops?
				 * This internal handler cannot be blocked,
				 * but the external handler told it to.
				 */
				 retval |= XFIH_BLOCKED;
			}
		}
		else {
			/* Lists of different length, not possible! */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(room_list, NULL);
	LLdelete(zero_list, NULL);
	LLdelete(access_list, NULL);
	LLdelete(count_list, NULL);
	LLdelete(time_list, NULL);
	LLdelete(name_list, NULL);
	LLdelete(type_list, NULL);
	LLdelete(gcid_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGC_RoomInfo(XFINST inst, struct XfirePacket *packet) {
	/* Full information about a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_70_01
	 * 
	 * TODO: POST handler, for the love of GODJonez
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*
	#define _XFABP xfgc
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	non0string *name_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x05));
	non0string *motd_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x4d));
	unsigned int *voice_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x4e));
	unsigned int *lvl_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x49));
	unsigned int *time_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x14));
	unsigned int *access_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x17));
	LinkedList *userid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x33));
	LinkedList *lvl_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x44));
	LinkedList *username_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x42));
	LinkedList *nickname_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x43));
	LinkedList *zero_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x32));
	unsigned int *voicehost_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x2f));
	unsigned int *voicequality_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x37));
	unsigned int *zero_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x5d));
	
	if (gcid_value && name_value && motd_value && voice_value && lvl_value &&
		time_value && access_value && userid_list && lvl_list && username_list &&
		nickname_list && zero_list && voicehost_value && voicequality_value &&
		zero_value) {
		if (userid_list->nodes == lvl_list->nodes && lvl_list->nodes == username_list->nodes &&
			username_list->nodes == nickname_list->nodes) {
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
				ListNode *userid_node;
				ListNode *lvl_node;
				ListNode *name_node;
				ListNode *nick_node;
				if (xfgc) {
					n0sfree(xfgc->name);
					xfgc->name = n0scopy(name_value);
					n0sfree(xfgc->motd);
					xfgc->motd = n0scopy(motd_value);
					xfgc->set_allowvoice = *voice_value;
					xfgc->set_defaultperm = *lvl_value;
					xfgc->info_timestamp = *time_value;
					/* For usercount use -1 because the list already contains self,
					 * but self is still sent as "joined" */
					xfgc->info_usercount = userid_list->nodes-1;
					xfgc->set_access = *access_value;
					/* TODO: Add voice info */
					
					/* Add users to chat room */
					/* Step 1: Clear the chat room user list */
					freeBTree(xfgc->users, NULL);
					xfgc->users = newBTree((CMPFUNC*)XfireAnalyze_ChatMemberCMPFUNC);
					/* Step 2: Loop through the users and add them */
					userid_node = userid_list->first;
					lvl_node = lvl_list->first;
					name_node = username_list->first;
					nick_node = nickname_list->first;
					while (userid_node) {
						struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *(unsigned int*)userid_node->x);
						if (!xff) {
							/* Unknown person, add to other_people and online_people */
							xff = XfireFriend();
							xff->userid = *(unsigned int*)userid_node->x;
							xff->username = n0scopy((non0string*)name_node->x);
							BTreeAdd(inst->other_people, xff);
							BTreeAdd(inst->online_people, xff);
						}
						/* Update nickname, just in case */
						n0sfree(xff->nickname);
						xff->nickname = n0scopy((non0string*)nick_node->x);
						/* Add to group chat members list */
						BTreeAdd(xfgc->users, XfireChatRoomUser(xff, *(unsigned int*)lvl_node->x));
						NEXT_NODE(userid_node);
						NEXT_NODE(lvl_node);
						NEXT_NODE(name_node);
						NEXT_NODE(nick_node);
					}
					/* include external handlers calling */
					#include "XfirePacketAnalyzeExt.inc"
				}
				else {
					/* Unknown room */
					retval |= XFIH_NONFATAL;
				}
			}
		}
		else {
			/* Different length lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Clean up */
	LLdelete(userid_list, NULL);
	LLdelete(lvl_list, NULL);
	LLdelete(username_list, NULL);
	LLdelete(nickname_list, NULL);
	LLdelete(zero_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGC_ChangeDefaultLvl(XFINST inst, struct XfirePacket *packet) {
	/* Admin changed default permission level of a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_72_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfgc, *lvl_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *lvl_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x13));
	
	if (gcid_value && lvl_value) {
		struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfgc) {
				xfgc->set_defaultperm = *lvl_value;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ChangeMOTD(XFINST inst, struct XfirePacket *packet) {
	/* Admin changed the Message Of The Day text of a chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_76_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, non0string*
	#define _XFABP xfgc, motd_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	non0string *motd_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x2e));
	
	if (gcid_value && motd_value) {
		struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfgc) {
				n0sfree(xfgc->motd);
				xfgc->motd = n0scopy(motd_value);
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_AllowVoice(XFINST inst, struct XfirePacket *packet) {
	/* Admin changed "Allow Voice Chat" setting
	 * 
	 * Obsolete?
	 * 
	 * http://xfireplus.com/xfwiki/index.php/Packet_77_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfgc, *voice_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *voice_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x2a));
	
	if (gcid_value && voice_value) {
		struct XfChatRoom *xfgc = XfireInst_FindChatRoom(inst, gcid_value);
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfgc) {
				xfgc->set_allowvoice = *voice_value;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_Kick(XFINST inst, struct XfirePacket *packet) {
	/* Admin kicked an user from chat room
	 * http://xfireplus.com/xfwiki/index.php/Packet_67_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT non0string*, unsigned int
	#define _XFABP gcid_value, *userid_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x18));
	
	if (gcid_value && userid_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* no internal handling, thanks to new Xfire updates :D */
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_VoiceStatus(XFINST inst, struct XfirePacket *packet) {
	/* Chat room voice chat status changed
	 * http://xfireplus.com/xfwiki/index.php/Packet_68_01
	 *
	 * SINGLE LIST :)
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, struct XfChatRoomVoice*, LinkedList*
	#define _XFABP xfcr, voice, users_list
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *type_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x34));
	unsigned int *enabled_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x25));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x2f));
	unsigned int *max_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x41));
	unsigned int *quality_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x37));
	non0string *voiceid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x5f));
	unsigned int *ip_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x70));
	unsigned int *port_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x71));
	LinkedList *users_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x33));
	
	if (gcid_value && type_value && enabled_value && userid_value && max_value &&
		quality_value && voiceid_value && ip_value && port_value && users_list) {
		
		struct XfChatRoomVoice *voice = XfireChatRoomVoice();
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		voice->session_id = n0scopy(voiceid_value);
		voice->host_type = *type_value;
		voice->voice_enabled = *enabled_value;
		voice->host_userid = *userid_value;
		voice->max_participants = *max_value;
		voice->voice_quality = *quality_value;
		voice->voice_ip = *ip_value;
		voice->voice_port = *port_value;
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				TreeNode *cuser_node = iterateTree(xfcr->users);
				
				FreeXfireChatRoomVoice(xfcr->voice);
				xfcr->voice = voice;
				
				/* Loop through all chat room users and update their voice
				 * participation status */
				
				while(cuser_node) {
					ListNode *user_node = users_list->first;
					struct XfChatRoomUser *xfcu = (struct XfChatRoomUser*)cuser_node->x;
					xfcu->using_voice = 0;
					
					while (user_node) {
						unsigned int userid = *(unsigned int*)user_node->x;
						if (xfcu->user->userid == userid) {
							xfcu->using_voice = 1;
							LLdelNode(user_node);
							break;
						}
						NEXT_NODE(user_node);
					}
					
					cuser_node = iterateTreeNext(cuser_node);
				}
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
		else {
			FreeXfireChatRoomVoice(voice);
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Clean up */
	LLdelete(users_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGC_VoiceHostInfo(XFINST inst, struct XfirePacket *packet) {
	/* Information about the voice chat hosting user
	 * http://xfireplus.com/xfwiki/index.php/Packet_6b_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, struct XfVoiceHostUser*
	#define _XFABP xfcr, host_user
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *extip_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x38));
	unsigned int *extport_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x39));
	unsigned int *intip_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x3a));
	unsigned int *intport_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x3b));
	unsigned int *status_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x3c));
	non0string *salt_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x3e));
	
	if (userid_value && gcid_value && extip_value && extport_value &&
		intip_value && intport_value && status_value && salt_value) {
		
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		struct XfVoiceHostUser *host_user = XfireVoiceHostUser();
		
		host_user->ip = *extip_value;
		host_user->port = *extport_value;
		host_user->localip = *intip_value;
		host_user->localport = *intport_value;
		host_user->status = *status_value;
		host_user->salt = n0scopy(salt_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				xfcr->voice->host_userid = *userid_value;
				FreeXfireVoiceHostUser(xfcr->voice->host_info);
				xfcr->voice->host_info = host_user;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
		else {
			FreeXfireVoiceHostUser(host_user);
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_VoiceLeave(XFINST inst, struct XfirePacket *packet) {
	/* Someone left group voice chat
	 * http://xfireplus.com/xfwiki/index.php/Packet_6d_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfcr, *userid_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	
	if (gcid_value && userid_value) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				struct XfFriend xff;
				struct XfChatRoomUser *xfu;
				xff.userid = *userid_value;
				xfu = XfireInst_FindChatRoomUserByXfFriend(inst, xfcr, &xff);
				xfu->using_voice = 0;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_VoiceJoin(XFINST inst, struct XfirePacket *packet) {
	/* Someone joined group voice chat
	 * http://xfireplus.com/xfwiki/index.php/Packet_6f_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfcr, *userid_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	
	if (gcid_value && userid_value) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				struct XfFriend xff;
				struct XfChatRoomUser *xfu;
				xff.userid = *userid_value;
				xfu = XfireInst_FindChatRoomUserByXfFriend(inst, xfcr, &xff);
				xfu->using_voice = 1;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_VoiceSession(XFINST inst, struct XfirePacket *packet) {
	/* Voice session ID for Xfire hosted voice servers
	 * http://xfireplus.com/xfwiki/index.php/Packet_7f_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, non0string*
	#define _XFABP xfcr, session_id
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	non0string *session_id = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x6b));
	
	if (gcid_value && session_id) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				n0sfree(xfcr->voice->session_id);
				xfcr->voice->session_id = n0scopy(session_id);
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_NameQuery(XFINST inst, struct XfirePacket *packet) {
	/* Response to chat room name availability query
	 * http://xfireplus.com/xfwiki/index.php/Packet_80_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int, unsigned int
	#define _XFABP *request_value, *response_value
	
	unsigned int *request_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x0b));
	unsigned int *response_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x0c));
	
	if (request_value && response_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* no internal handling */
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ChangePassword(XFINST inst, struct XfirePacket *packet) {
	/* The chat room was password protected or unprotected
	 * http://xfireplus.com/xfwiki/index.php/Packet_81_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfcr, *protected_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *protected_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x5f));
	
	if (gcid_value && protected_value) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				xfcr->set_protected = *protected_value;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ChangeAccessibility(XFINST inst, struct XfirePacket *packet) {
	/* The chat room was made private or public
	 * http://xfireplus.com/xfwiki/index.php/Packet_82_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfcr, *protected_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *protected_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x17));
	
	if (gcid_value && protected_value) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				xfcr->set_access = *protected_value;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_DenyInvite(XFINST inst, struct XfirePacket *packet) {
	/* Confirmation that the client has denied the chat room invitation
	 * http://xfireplus.com/xfwiki/index.php/Packet_83_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT non0string*
	#define _XFABP gcid_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	
	if (gcid_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* no internal handling */
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ChangeSilence(XFINST inst, struct XfirePacket *packet) {
	/* The chat room was made silent or free for talk
	 * http://xfireplus.com/xfwiki/index.php/Packet_84_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int, unsigned int
	#define _XFABP xfcr, *userid_value, *silenced_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	unsigned int *silenced_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x16));
	
	if (gcid_value && userid_value && silenced_value) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				xfcr->set_silenced = *silenced_value;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}

int XfireAnalyze_hGC_ChangeJoinPartMsg(XFINST inst, struct XfirePacket *packet) {
	/* The chat room was made to hide or show the users leaving or joining messages
	 * http://xfireplus.com/xfwiki/index.php/Packet_85_01
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfChatRoom*, unsigned int
	#define _XFABP xfcr, *show_value
	
	non0string *gcid_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x04));
	unsigned int *show_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x1b));
	
	if (gcid_value && show_value) {
		struct XfChatRoom *xfcr = XfireInst_FindChatRoom(inst, gcid_value);
		
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xfcr) {
				xfcr->set_showjoinpart = *show_value;
			}
			else {
				/* Unknown room */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clean up */
	
	return retval;
}
